package com.handler;

import com.common.R;
import com.common.ResultCode;
import com.exception.ApplicationException;
import com.exception.ResourceNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/*
 统一异常处理
 这里只序列化：ResultCode和message。注意：不传异常栈信息。
 */
@Slf4j
@RestControllerAdvice
public class GlobalRestExceptionHandler {

	// 不支持的请求方法
	@ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public R<Void> httpRequestMethodNotSupportedException
					(HttpRequestMethodNotSupportedException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.httpRequestMethodNotSupportedException.", e);

		return R.fail(ResultCode.METHOD_NOT_SUPPORTED).message(e.getMessage());
	}

	// 不支持的媒体类型
	@ResponseStatus(code = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
	@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
	public R<Void> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.httpMediaTypeNotSupportedException.", e);

		return R.fail(ResultCode.MEDIA_TYPE_NOT_SUPPORTED).message(e.getMessage());
	}

	// GET请求中@Valid验证参数失败后抛出异常BindException
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
	@ExceptionHandler(value = { BindException.class })
	public R<Void> handlerBindException(BindException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handlerBindException.", e);

		StringBuilder messages = new StringBuilder(1024);
		List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
		for (FieldError error : fieldErrors) {
			messages.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("\n\r");
		}
		return R.fail(ResultCode.PARAM_VALID_ERROR).message(messages.toString());
	}

	// @RequestParam参数校验失败后抛出异常ConstraintViolationException
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
	@ExceptionHandler(value = { ConstraintViolationException.class })
	public R<Void> handlerConstraintViolationException(ConstraintViolationException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handlerConstraintViolationException.", e);

		StringBuilder messages = new StringBuilder(1024);
		Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
		for (ConstraintViolation error : constraintViolations) {
			messages.append(error.getPropertyPath().toString()).append(": ").append(error.getMessage()).append("\n\r");
		}
		return R.fail(ResultCode.PARAM_VALID_ERROR).message(messages.toString());
	}

	// @RequestBody参数校验失败后抛出异常MethodArgumentNotValidException
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
	@ExceptionHandler(value = { MethodArgumentNotValidException.class })
	public R<Void> handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handlerMethodArgumentNotValidException.", e);

		StringBuilder messages = new StringBuilder(1024);
		List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
		for (FieldError error : fieldErrors) {
			messages.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("\n\r");
		}
		return R.fail(ResultCode.PARAM_VALID_ERROR).message(messages.toString());
	}

	// 服务器没有发现资源 404
	@ResponseStatus(value = HttpStatus.NOT_FOUND)
	@ExceptionHandler(NoHandlerFoundException.class)
	public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handleNoHandlerFoundException.", e);

		return R.fail(ResultCode.NOT_FOUND).message(e.getMessage());
	}

	// 应用未发现资源ResourceNotFoundException
	@ResponseStatus(value = HttpStatus.NOT_FOUND)
	@ExceptionHandler(ResourceNotFoundException.class)
	public R<Void> handleResourceNotFoundException(ResourceNotFoundException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handleResourceNotFoundException.", e);

		return R.fail(ResultCode.NOT_FOUND).message(e.getMessage());
	}

	// 处理自定义异常ApplicationException
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(ApplicationException.class)
	public R<ApplicationException> handleApplicationException(ApplicationException e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handleApplicationException.", e);

		// 异常中指定的错误代码，消息按优先级合并在异常的message中
		return R.fail(e.getResultCode()).message(e.getMessage());
	}

	// 通常在最后的方法处理最大的Exception，保证兜底
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(value = Throwable.class)
	public R<Void> handleException(Throwable e) {
		// 记录异常栈信息，供开发人员查错
		log.error("ExceptionControllerAdvice.handleException.", e);

		return R.fail(ResultCode.UNKNOWN).message(e.getMessage());
	}
}